#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "field.h"
field_t *field_new(void)
{
    field_t *field = NULL;
    field = calloc(sizeof(field_t), 1);
    if (field == NULL) {
        err_print("Oh, no calloc faild.\n");
    }
    return field;
}

void field_free(field_t *field)
{
    if (field) {
        free(field);
    }
}

void field_insert_before(field_t *cur, field_t *field)
{
    field->next = cur;
    field->prev = cur->prev;
    cur->prev = field;
}

void field_insert_after(field_t *cur, field_t *field)
{
    if (cur && field) {
        field->prev = cur;
        field->next = cur;
        cur->next = field;
    }
}

void field_remove(field_t *field)
{
    field_t *prev, *next;
    prev = field->prev;
    next = field->next;
    if (prev) {
        prev->next = field->next;
    } else { /* header */
        next->prev = NULL;
    }

    if (next) {
        next->prev = field->prev;
    } else { /* tail */
        next->next = NULL;
    }
}

void field_append(field_t *cur, field_t *field)
{
    while (cur->next) {
        cur = cur->next;
    }
    cur->next = field;
    field->prev = cur;
}

void field_prepend(field_t *cur, field_t *field)
{
    while (cur && cur->prev) {
        cur = cur->prev;
    }
    cur->prev = field;
    field->next = cur;
}

void field_move_before(field_t *field)
{
    field_t *prev = field->prev;
    if (prev) {
        field->prev = prev->prev;
        field->next = prev;
        prev->prev = field;
        prev->next = field->next;
    }
}
void field_move_after(field_t *field)
{
    field_t *next = field->next;
    if (next) {
        field->prev = next;
        field->next = next->next;
        next->prev = field->prev;
        next->next = field;
    }
}

void field_add_child(field_t *cur, field_t *child)
{
    field_t *field = cur->children;
    if (field) {
        field_append(field, child);
    } else {
        cur->children = child;
    }
}

void field_add_parent(field_t *cur, field_t *parent)
{
    field_t *field = cur->parents;
    field_prepend(field, parent);
}

field_t *field_get_header(field_t *field)
{
    while (field->prev) {
        field = field->prev;
    }
    return field;
}

field_t *field_get_tail(field_t *field)
{
    while (field->next) {
        field = field->next;
    }
    return field;
}

void field_show(field_t *field)
{
    while (field) {
        field_t *child = field->children;
        if (field->name) {
            printf("%s %d %d", field->name, field->type, field->count);
            switch (field->type) {
                case FIELD_TYPE_INT:
                    break;
                case FIELD_TYPE_DENPENDENCY:
                    printf(" %s", field->desc.dp.depend_name);
                    break;

            }
            printf("\n");
        } else {
            printf("undefine %d\n", field->type);
        }
        if (child) {
            field_show(child);
        }
        field = field->next;
    }
}

field_t *field_search_by_name(field_t *field, char *name)
{
    field_t *f = NULL;
    while (field) {
        field_t *child = field->children;
        if (field->name) {
            if (strcmp(field->name, name) == 0) {
                return field;
            }
        }
        if (child) {
            f = field_search_by_name(child, name);
            if (f) {
                return f;
            }
        }
        field = field->next;
    }
    return f;
}
static uint64_t read_unsigned_bits_littleendian(uint8_t *data, size_t *pos, char *bit_offset, unsigned count) {
    uint64_t retval = 0;
    unsigned int out_idx=0;
    while(count>0) {
        if((*bit_offset) == 0 && (count &7) ==0) {
            retval|= data[*pos] << out_idx;
            out_idx+=8;
            (*pos) ++;
            count-=8;
        }
        else {
            //This can use a lot of performance love
//TODO: test this
            retval |= ((data[*pos] >> (*bit_offset)) & 1) << out_idx;
            out_idx++;
            count--;
            (*bit_offset)++;
            if(*bit_offset >7) {
                (*bit_offset) -= 8;
                (*pos)++;
            }
        }
    }
    return retval;

}

/*
static uint64_t read_unsigned_bits(uint8_t *data, size_t *pos, char *bit_offset, unsigned count) {
        uint64_t retval = 0;
        unsigned int out_idx=count;
        //TODO: Implement little endian too
        //Count LSB to MSB
        while(count>0) {
                if((*bit_offset) == 0 && (count &7) ==0) {
                        out_idx-=8;
                        retval|= data[*pos] << out_idx;
                        (*pos) ++;
                        count-=8;
                }
                else{
                        //This can use a lot of performance love
                        //TODO: implement other endianesses
                        out_idx--;
                        retval |= ((data[*pos] >> (7-*bit_offset)) & 1) << out_idx;
                        count--;
                        (*bit_offset)++;
                        if(*bit_offset > 7){
                                (*bit_offset) -= 8;
                                (*pos)++;
                        }
                }
        }
    return retval;
}
*/
int parse_data_by_fields(field_t *field, uint8_t **pdata)
{
    size_t pos = 0;
    uint8_t *data = *pdata;
    char bit_offset = 0;
    while (field) {
        field_t *child = field->children;
        if (field->name) {
            printf("%-4d %-16s:", pos, field->name);
            switch (field->type) {
                case FIELD_TYPE_INT:
                    {
                        int iv = (int) read_unsigned_bits_littleendian(data, &pos, &bit_offset, field->count);
                        printf("%d 0x%x(%d)", field->count, iv, iv);
                    }
                    break;
                case FIELD_TYPE_UINT:
                    {
                        uint32_t uv = (uint32_t) read_unsigned_bits_littleendian(data, &pos, &bit_offset, field->count);
                        printf("%d 0x%x(%d)", field->count, uv, uv);
                    }
                    break;
                case FIELD_TYPE_DENPENDENCY:
                    printf(" %s", field->desc.dp.depend_name);
                    break;

            }
            printf("\n");
        } else {
        }
        *pdata = data + pos;
        if (child) {
            parse_data_by_fields(child, pdata);
            data = *pdata;
        }
        field = field->next;
    }

}


void field_set_dependency(field_t *field)
{
}

/*
field_is_valid();
field_set_name();
*/

